-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cpu/atmega_common: RTT and RTC support #8842
Conversation
I have found that the local copy of cpu/atmega_common/avr-libc-extra/time.h conflicts with the system avr-libc, and makes the time.h functions produce garbage output. Since this was actually used almost nowhere, I removed it. There was a minor struct that is not provided by avr-libc time.h, which I moved into cpu/atmega_common/include/sys/time.h. I can split this off into another PR, if you think it is necessary. I have fixed all of the problems listed in my first comment, except for the lagged messages. There is one final tricky bug I am trying to track down: there is a race condition that is related to use of xtimer_usleep that I have not been able to fix. When xtimer is disabled, everything is fine; when xtimer is enabled and debug is enabled, everything is fine; when only xtimer is enabled, some checks hang and alarms never trigger. |
time.h change is now #8857 |
Regarding using RTC as a wrapper for RTT, see also: |
Unlike a simple RTC wrapper like in cpu/kinetis, this implementation can be used concurrently to the RTT. This is because it keeps time independently of the RTT, and it utilizes a different counter channel on the timer. |
02743a1
to
695a25f
Compare
I have found that the xtimer_usleep problem is an instance of #6526 and not directly related to the code here. The hang on tests/periph_rtc is solved by turning off xtimer, or adding a while(1) to the end of main. It seems to be the same problem every other interrupt has after exiting main. I don't think there are any remaining outstanding issues. As soon as #8857 merges, it should be ready for CI build testing. |
It seems that moving rtc_init conflicts with the organization of the init systems (see #8914). It is also placed early enough that xtimer cannot be used. Swapping threads is not going to be useful because other threads have not been started (it is before kernel_init). Is it acceptable in this case to spin for 30-60ms while waiting for the busy flags to clear, or is there another alternative? This is required by the hardware for safe access to the asynchronous registers. |
OK, I briefly skimmed the ATMega1284p data sheet to find out more about the hardware.
From the data sheet:
My conclusion from that text is that the synchronization takes 1 < t <= 2 [TOSC1 ticks], which is running at 32768 Hz, meaning that the syncronization is between 30.5 µs and 61 µs. The nomenclature in the rest of that chapter is that TOSC1 is the input to the prescaler, and not the divided counter signal. Please tell me if you have any indication that the synchronization is indeed using the divided clock signal instead of the unscaled one. To me it seems like a pretty bad hardware design choice to let the silicon take 1/16 second to read/write a value from a timer. AFAICT using TOSC1 directly instead of the divided signal would not introduce any asynchronous problems since the TOSC1 signal and the scaled clock signal are synchronous compared to each other, since they are derived from the same signal, albeit with some small, non-zero, phase shift because of propagation delays in the prescaler. If my conclusions are true, you should drop the xtimer calls completely and just spin for the sync flags to clear, 60 µs is definitely OK to spin while setting a slow 32 Hz timer target in my opinion. It is worth mentioning this spin in the documentation or code comments somewhere so that developers can find out about this without studying the details of the data sheet. I suggest putting it in the documentation block for the Also, please consider any possible race conditions when using the RTC and RTT from two different threads. It may be necessary to mask interrupts in some parts of the low level hardware accesses to prevent clobbering each others settings, especially in read-modify-write situations. |
@gebart Sorry for my confusion. I thought I had seen an entry indicating that the synchronization unit used the prescaler and not TOSC1 directly, but I was mistaken. __asynch_wait now spins, and I put a comment on about how long that will take. The only remaining problem is solved by #8857. Let me know when you want me to squash. There shouldn't be any race conditions with using RTT and RTC together since the RTC does not actually change any RTT settings. The alarm used by the RTC is handled by counter B, while the alarm used by the RTT is counter A. So long as nothing runs rtt_poweroff the worst that can happen is a 0-8 glitch in timing when running rtt_set_counter while the RTC is running, but this should not prevent any ISRs from firing properly (just delay them). I suppose I could add some code to compensate for the timing glitch, but that would require editing RTC variables in the RTT code. |
@ZetaR60 I don't think you need to handle that case here and now, I just wanted to make sure you had considered what the potential races are in this implementation. I just realized something, you need to rename __async_wait, double underscores are forbidden in identifiers in C, outside of the compiler provided macros and support libs |
Changed to _asynch_wait. I was a bit confused about single and double underscores, considering the usage in e.g. cpu/atmega_common/thread_arch.c. Am I correct in thinking that single underscores are preferred for naming static functions in RIOT libraries? Also ran uncrustify. |
c83b2fe
to
4eadaa7
Compare
Squashed with requested changes incorporated. |
@ZetaR60 it is easier to review the PR if you don't squash your changes until you get a request to squash by a maintainer. Otherwise the person reviewing will need to try to remember what the code was like before. Making reviewing easy helps getting your PR merged more quickly. |
4eadaa7
to
b212374
Compare
@gebart Sorry. I thought that the amount of time waited was enough. I managed to unsquash from a backup. |
ping @kaspar030 @gebart now that the release time crunch is over. |
@gebart Is there anything else that this PR needs? |
My latest commits should fix this issue. I believe that all code is now safe from timer events, other ISRs interrupting the current thread, and running the code with interrupts disabled or in ISR mode. It is not safe from concurrent access by multiple threads, except in the case where only state reads are occurring. However, so far as I can tell this is not generally expected of RIOT APIs, and it can be made concurrent access safe by disabling ISRs during access (like everything else). |
It seems that making the code safe from concurrent access by multiple threads was easier than I thought. I think I have handled all of the various conditions that can occur; this is fairly difficult though, since there is multiple asynchronous considerations at almost every line of code, so I may have missed some things. @maribu @jcarrano et al: I would like to squash and rebase as the list of changes is getting quite messy and out of date. Please let me know if this is acceptable. |
I'd say go ahead. |
|
||
/* Detect rollover after time_tmp read. If condition is met, then rollover | ||
* just occured, and it is safe to read again now. */ | ||
} while ((time_tmp != time_cnt) || (irq_flag != (TIFR2 & (1 << TOV2)))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I´m quite sure that TOV2
will ever be 0. But i might be wrong.
Bit 0 – TOV: Timer/Counter2, Overflow Flag
The TOV bit is set (one) when an overflow occurs in Timer/Counter2. TOV is cleared by hardware when
executing the corresponding interrupt handling vector.
As the interrupt is enabled TOV2
will be cleared. Polling it only is usefull when no IRQ routine
is used, imho.
Maybe handling like in xtimer would be an idea.
Line 89 in 8755a26
do { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TOV2
is checked in case interrupts are disabled. These two checks are not really polls, because the condition is only expected to occur zero or one times. It is just more space efficient than using if
, since the compiler is not allowed to optimize this due to volatile.
d7abe34
to
dc4f121
Compare
Is there a TL;DR for the discussion? A rebase would be in order any way. |
When I run this on
I'm a bit surprised by how long it stays in the |
@benpicco I think board is missing the 32kHz xtal needed for TIM2. This timer can be asynchronous and work even if we put power hungry timers in sleep mode. As a fallback for RDC is essential. But challenge is to sync the clocks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see two irq_disable() in rtc_clear_alarm. Can you elaborate on this?
PING everyone, should we get this done? |
RTT support has been merged now, should be possible to base RTC support off that if you set |
Maybe we could also just provide the RTC interface on top of the RTT? Unless the hardware internationally uses years, hours, minutes and seconds as units, I would consider the RTT interface the better option. And if one really wants an RTC like interface, a generic wrapper on top of RTT would solve this for all clock hardware (except those really internally using the broken up time / date fornat). |
Something like this? int rtc_set_time(struct tm *time)
{
uint32_t t = RTT_SEC_TO_TICKS(mktime(time));
rtt_set_counter(t);
return 0;
}
int rtc_get_time(struct tm *time)
{
time_t t = RTT_TICKS_TO_SEC(rtt_get_counter());
gmtime_r(&t, time);
return 0;
}
int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg)
{
time_t t = mktime(time);
rtt_set_alarm(RTT_SEC_TO_TICKS(t), cb, arg);
return 0;
}
int rtc_get_alarm(struct tm *time)
{
time_t t = RTT_TICKS_TO_SEC(rtt_get_alarm());
gmtime_r(&t, time);
return 0;
} (We can replace the POSIX functions with |
There's already something like this somewhere in the codebase at least here: |
The RTT overflows too fast to use it like that (the slowest is 32 Hz and it only has 24 bit), but we can make use of |
Both RTT and RTC support have now been merged. |
This adds RTT and RTC support to the ATmegas. It is currently partially working on ATMega1284P using the mega-xplained board. So far as I can tell, the timer used with the real-time crystal is the same across all supported ATmegas, so if another board is added with a RTC crystal it should work right away. The crystal cannot be added to any of the Arduino boards without soldering, unfortunately.
There are a few problems with the code that I need to work out, but I need some advice on how to proceed:
The RTT code is based on some prior work by Robert Hartung in #7604